package interview.i_003_concurrent;

import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

/**
 * 要求用线程顺序打印 A1B2C3....Z26
 */
class T005_OrderPrint {
    Thread t1 = null, t2 = null;
    char[] aI = "1234567".toCharArray();
    char[] aC = "ABCDEFG".toCharArray();

    @Test
    void demo1() {
        t1 = new Thread(() -> {
            for (char c : aI) {
                System.out.print(c);
                LockSupport.unpark(t2); // 唤醒 T2
                LockSupport.park(); // T1 阻塞
            }
        });
        t2 = new Thread(() -> {
            for (char c : aC) {
                LockSupport.park(); // T2 阻塞
                System.out.print(c);
                LockSupport.unpark(t1); // 唤醒 T1
            }
        });
        t1.start();
        t2.start();
    }

    enum ReadyToRun {T1, T2}

    volatile ReadyToRun r = ReadyToRun.T1; // 必须 volatile

    @Test
    void demo2() {
        new Thread(() -> {
            for (char c : aI) {
                while (r != ReadyToRun.T1) {
                }
                System.out.print(c);
                r = ReadyToRun.T2;
            }
        }).start();
        new Thread(() -> {
            for (char c : aC) {
                while (r != ReadyToRun.T2) {
                }
                System.out.print(c);
                r = ReadyToRun.T1;
            }
        }).start();
    }


    BlockingQueue<String> q1 = new ArrayBlockingQueue<>(1);
    BlockingQueue<String> q2 = new ArrayBlockingQueue<>(1);

    @Test
    void demo3() {
        new Thread(() -> {
            for (char c : aI) {
                System.out.print(c);
                try {
                    q1.put("ok");
                    q2.take();
                } catch (InterruptedException ignored) {
                }
            }
        }).start();
        new Thread(() -> {
            for (char c : aC) {
                try {
                    q1.take();
                    System.out.print(c);
                    q2.put("ok");
                } catch (InterruptedException ignored) {
                }
            }
        }).start();
    }

    @Test
    void demo4() {
        AtomicInteger threadNo = new AtomicInteger(1);
        new Thread(() -> {
            for (char c : aI) {
                while (threadNo.get() != 1) {
                }
                System.out.print(c);
                threadNo.set(2);
            }
        }).start();
        new Thread(() -> {
            for (char c : aC) {
                while (threadNo.get() != 2) {
                }
                System.out.print(c);
                threadNo.set(1);
            }
        }).start();
    }

    @Test
    void demo5() throws IOException, InterruptedException {
        PipedInputStream input1 = new PipedInputStream();
        PipedInputStream input2 = new PipedInputStream();
        PipedOutputStream output1 = new PipedOutputStream();
        PipedOutputStream output2 = new PipedOutputStream();
        input1.connect(output2);
        input2.connect(output1);
        t1 = new Thread(() -> {
            byte[] buffer = new byte[1];
            try {
                for (char c : aI) {
                    input1.read(buffer);
                    System.out.print(new String(buffer));
                    output1.write(c);
                }
            } catch (IOException ignored) {
            }
        });
        t2 = new Thread(() -> {
            byte[] buffer = new byte[1];
            try {
                for (char c : aC) {
                    output2.write(c);
                    input2.read(buffer);
                    System.out.print(new String(buffer));
                }
            } catch (IOException ignored) {
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }


    @Test
    void demo6() { // 无法保证
        Exchanger<Character> exchanger = new Exchanger<>(); // JDK 1.5 开始提供的一个用于两个工作线程之间交换数据的封装工具类
        new Thread(() -> {
            for (char c : aI) {
                try {
                    System.out.print(exchanger.exchange(c));
                } catch (InterruptedException ignored) {
                }
            }
        }).start();
        new Thread(() -> {
            for (char c : aC) {
                try {
                    System.out.print(exchanger.exchange(c));
                } catch (InterruptedException ignored) {
                }
            }
        }).start();
    }

    @Test
    void demo7() {
        TransferQueue<Character> queue = new LinkedTransferQueue<>(); // 生产者会一直阻塞直到所添加到队列的元素被某一个消费者所消费
        new Thread(() -> {
            try {
                for (char c : aI) {
                    System.out.print(queue.take());
                    queue.transfer(c);
                }
            } catch (InterruptedException ignored) {
            }
        }).start();
        new Thread(() -> {
            try {
                for (char c : aC) {
                    queue.transfer(c);
                    System.out.print(queue.take());
                }
            } catch (InterruptedException ignored) {
            }
        }).start();
    }
}